#lang sicp
;; data-directd

(define table '())
(define (make-record op type v) (list op type v))
(define (op-record v) (car v))
(define (type-record v) (cadr v))
(define (value-record v) (caddr v))
(define (get op type)
  (define (iter table)
    (cond ((null? table) #F)
          ((and (eq? (op-record (car table)) op)
                (eq? (type-record (car table)) type))
           (car table))
          (else (iter (cdr table)))))
  (let ((res (iter table)))
    (if res
        (value-record res)
        #F)))
(define (set op type v)
  (set! table (cons (make-record op type v) table)))

;; generic procedure
(define (real-part x)
  (apply-generic 'real-part x))
(define (imag-part x)
  (apply-generic 'imag-part x))
(define (magnitude x)
  (apply-generic 'magnitude x))
(define (angle x)
  (apply-generic 'angle x))

;;
(define (apply-generic op v)
  ((get op (get-type v)) (get-contents v)))

(define (get-type x) (car x))
(define (get-contents x) (cdr x))
(define (attach-type type x) (cons type x))

(define (make-from-mag-ang m a)
  (attach-type 'polar ((get 'make-from-mag-ang 'polar) m a)))
(define (make-from-real-imag m a)
  (attach-type 'rectangular ((get 'make-from-real-imag 'rectangular) m a)))


;;
(define (install-complex-polar)
  (define (real-part z) (* (magnitude z) (cos (angle z))))
  (define (imag-part z) (* (magnitude z) (sin (angle z))))
  (define (magnitude z) (car z))
  (define (angle z) (cdr z))
  (define (make-from-real-imag x y)
    (cons (sqrt (+ (square x) (square y)))
          (atan y x)))
  (define (make-from-mag-ang r a) (cons r a))

  ;; install
  (define type 'polar)
  (set 'real-part type real-part)
  (set 'imag-part type imag-part)
  (set 'magnitude type magnitude)
  (set 'angle type angle)
  (set 'make-from-real-imag type make-from-real-imag)
  (set 'make-from-mag-ang type make-from-mag-ang)
  'done
)

;;
(define (install-complex-rectangular)
  (define (real-part z) (car z))
  (define (imag-part z) (cdr z))
  (define (magnitude z)
    (sqrt (+ (square (real-part z))
             (square (imag-part z)))))
  (define (angle z)
    (atan (imag-part z) (real-part z)))
  (define (make-from-real-imag x y) (cons x y))
  (define (make-from-mag-ang r a)
    (cons (* r (cos a)) (* r (sin a))))

  ;;install
  (define type 'rectangular)
  (set 'real-part type real-part)
  (set 'imag-part type imag-part)
  (set 'magnitude type magnitude)
  (set 'angle type angle)
  (set 'make-from-real-imag type make-from-real-imag)
  (set 'make-from-mag-ang type make-from-mag-ang)
  'done
)


;;
(define (square x) (* x x))


;;test
(install-complex-rectangular)
(install-complex-polar)

(define a (make-from-real-imag 3 4))
(define b (make-from-mag-ang 5 0.9272))